package util

import (
	"crypto/hmac"
	"crypto/sha1"
	"encoding/base64"
	"github.com/google/uuid"
	"net/url"
	"sort"
	"strings"
	"time"
)

const APIVersion = "v1"
const ISO8601DateFormat = "2006-01-02T15:04:05Z"
const Encoding = "UTF-8"
const ResponseFormat = "JSON"
const RequestTimeout = 120 // 请求超时时间（秒）

func Verify(httpMethod string, accessKeySecret string, parameters map[string]string) bool {
	timeStamp := parameters["TimeStamp"]
	if len(timeStamp) == 0 {
		MainCtx.Logger.Error("timestamp empty")
		return false
	}
	timestamp, err := parseTimestamp(timeStamp)
	if err != nil {
		MainCtx.Logger.Error("timestamp parse error: " + err.Error())
		return false
	}
	if time.Now().UnixMilli()-timestamp.UnixMilli() > RequestTimeout*1000 {
		MainCtx.Logger.Error("timestamp time out ")
		return false
	}
	requestSignature := parameters["Signature"]
	delete(parameters, "Signature")
	signatureMethod := parameters["SignatureMethod"]
	var method string
	if strings.EqualFold(signatureMethod, "HMAC-SHA1") {
		method = "HmacSHA1"
	} else {
		method = signatureMethod
	}
	newSignature := calculateSignature(httpMethod, accessKeySecret, method, parameters)
	return strings.EqualFold(newSignature, requestSignature)
}
func Sign(accessKeyId string, accesskeySecret string, action string, httpMethod string,
	parameters map[string]string) map[string]string {
	parameters["Action"] = action
	parameters["Version"] = APIVersion
	parameters["AccessKeyId"] = accessKeyId
	parameters["TimeStamp"] = formatIso8601Date(time.Now())
	parameters["SignatureMethod"] = "HMAC-SHA1"
	parameters["SignatureVersion"] = "1.0"
	parameters["SignatureNonce"] = uuid.New().String()
	parameters["Format"] = ResponseFormat
	parameters["Signature"] = calculateSignature(httpMethod, accesskeySecret, "HmacSHA1", parameters)
	return parameters
}

func formatIso8601Date(now time.Time) string {
	return now.UTC().Format(ISO8601DateFormat)
}

func calculateSignature(method string, accessKeySecret string, ALGORITHM string, parameters map[string]string) string {
	keys := make([]string, 0)
	for key, _ := range parameters {
		keys = append(keys, key)
	}
	sort.Strings(keys)
	const SEPARATOR = "&"
	var builder strings.Builder
	for _, key := range keys {
		builder.WriteString(SEPARATOR)
		builder.WriteString(percentEncode(key, parameters[key]))
	}
	var stringToSign strings.Builder
	stringToSign.WriteString(method)
	stringToSign.WriteString(SEPARATOR)
	stringToSign.WriteString(percentEncodeSingle("/"))
	stringToSign.WriteString(SEPARATOR)
	stringToSign.WriteString(builder.String()[1:])
	signature := calculate(accessKeySecret+SEPARATOR, ALGORITHM, stringToSign.String())
	return signature
}

func calculate(key string, algorithm string, sign string) string {
	h := hmac.New(sha1.New, []byte(key))
	h.Write([]byte(sign))
	hmacValue := h.Sum(nil)
	encode := base64.StdEncoding.EncodeToString(hmacValue)
	return encode
}
func percentEncodeSingle(str string) string {
	return url.QueryEscape(str)
}
func percentEncode(key string, value string) string {
	values := url.Values{}
	values.Set(key, value)
	encode := values.Encode()
	encode = strings.ReplaceAll(encode, "+", "%20")
	encode = strings.ReplaceAll(encode, "*", "%2A")
	encode = strings.ReplaceAll(encode, "%7E", "~")
	return encode
}

// 解析请求时间戳, 用于判定是否超时
func parseTimestamp(stamp string) (time.Time, error) {
	// 使用UTC时区
	loc, err := time.LoadLocation("UTC")
	if err != nil {
		return time.Time{}, err
	}
	return time.ParseInLocation(ISO8601DateFormat, stamp, loc)
}
